package com.java.common.boot.advice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.java.common.constant.HttpStatus;
import com.java.common.lang.domain.AjaxResult;
import com.java.common.lang.exception.BaseException;
import com.java.common.lang.exception.CustomException;
import com.java.common.utils.ServletUtils;
import com.java.common.utils.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * 
 * @author Administrator
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    private final String PARAMETER_ERROR_MESSAGE = "参数错误";

    /**
     * 基础异常
     */
    @ExceptionHandler(BaseException.class)
    public AjaxResult baseException(BaseException e) {
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(CustomException.class)
    public AjaxResult businessException(CustomException e) {
        if (StringUtils.isNull(e.getCode())) {
            return AjaxResult.error(e.getMessage());
        }
        return AjaxResult.error(e.getCode(), e.getMessage());
    }

    /**
     * 404异常处理<br>
     * 返回ResponseEntity，保留网络请求404状态码
     * 
     * @author yilabao
     * @date 2021年1月23日
     * @param e
     * @return ResponseEntity<ReturnVO<String>>
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    public Object handlerNoFoundException(NoHandlerFoundException e) {
        LOG.error(e.getMessage(), e);
        if (!ServletUtils.isAjaxRequest()) {
            return new ModelAndView("error/404.html");
        }
        AjaxResult errorBody = AjaxResult.error(HttpStatus.NOT_FOUND, "路径不存在，请检查路径是否正确");
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorBody);
    }

    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAuthorizationException(AccessDeniedException e) {
        LOG.error(e.getMessage());
        return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权");
    }

    @ExceptionHandler(AccountExpiredException.class)
    public AjaxResult handleAccountExpiredException(AccountExpiredException e) {
        LOG.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }

    @ExceptionHandler(UsernameNotFoundException.class)
    public AjaxResult handleUsernameNotFoundException(UsernameNotFoundException e) {
        LOG.error(e.getMessage(), e);
        return AjaxResult.error(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult validatedBindException(BindException e, HttpServletRequest request) {
        List<String> messageList = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList());
        String message = String.valueOf(messageList);
        LOG.error("请求地址：{}，参数异常：{}", request.getRequestURI(), message);
        return AjaxResult.error(returnMessage(message));
    }

    /**
     * 无效参数异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Object validExceptionHandler(MethodArgumentNotValidException e) {
        LOG.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return AjaxResult.error(returnMessage(message));
    }

    /**
     * 无效参数异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Object illegalArgumentExceptionHandler(IllegalArgumentException e) {
        LOG.error(e.getMessage(), e);
        String message = e.getMessage();
        return AjaxResult.error(message);
    }

    /**
     * 请求Body异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Object validExceptionHandler(HttpMessageNotReadableException e) {
        LOG.error(e.getMessage(), e);
        return AjaxResult.error(PARAMETER_ERROR_MESSAGE);
    }

    /**
     * 普通异常
     */
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e) {
        LOG.error(e.getMessage(), e);
        if (!ServletUtils.isAjaxRequest()) {
            return new ModelAndView("error/500.html");
        }
        return AjaxResult.error();
    }

    /**
     * 返回消息
     *
     * @param message
     * @return String
     * @author yilabao
     * @date 2022年5月23日
     */
    private String returnMessage(String message) {
        final String exception = "exception";
        final String java = "java";
        if (StringUtils.containsAny(message, exception, java)) {
            return PARAMETER_ERROR_MESSAGE;
        }
        return message;
    }

}
